"""
实现一个 4 层神经网络，其中：

- 2 层隐藏层
- 每层的神经元个数均为 3
- 使用单个模拟样本数据，学习前向传播的代码表示，和反向传播的数学原理
- 第一层隐层使用 ReLU 激活，第二层和输出层使用 Sigmoid 激活
- 损失函数使用 ？？
"""

import numpy as np


# sigmod 函数
def sigmod(x):
    return 1 / (1 + np.exp(-x))


# 前向传播
def forward_propagation(x, w1, b1, w2, b2, w3, b3):
    # 输入层传播到第一隐藏层
    h1_in = np.dot(x, w1) + b1
    # 第一隐层激活（ReLU）
    h1_out = np.maximum(0, h1_in)

    # 第一隐层传播到第二隐层
    h2_in = np.dot(h1_out, w2) + b2
    # 第二隐层激活（sigmod）
    h2_out = sigmod(h2_in)

    # 第二隐层传播到输出层
    l_in = np.dot(h2_out, w3) + b3
    # 输出层激活（sigmod）
    l_out = sigmod(l_in)

    return {
        "l_out": l_out,
        "h1_out": h1_out,
        "h2_out": h2_out,
    }


# 反向传播
def backward_propagation(y, pred, w1, b1, w2, b2, w3, b3, alpha):
    w3 = w3 - alpha * (
        np.dot(
            np.dot(pred["h2_out"].reshape(-1, 1), np.ones((1, 3))),
            (
                np.dot(np.dot((pred["l_out"] - y), pred["l_out"]), (1 - pred["l_out"]))
            ).reshape(3, 1),
        )
    )
    print(w3)


if __name__ == "__main__":
    # 训练数据准备
    X = np.array([0.1, 0.2, 0.7])
    Y = np.array([1.0, 0.0, 0.0])

    # 参数初始化
    w1 = np.random.rand(3, 3)  # 生成 3×3 的 0~1 的随机数
    b1 = np.array([1.0, 1.0, 1.0])
    w2 = np.random.rand(3, 3)
    b2 = np.array([1.0, 1.0, 1.0])
    w3 = np.random.rand(3, 3)
    b3 = np.array([1.0, 1.0, 1.0])

    # 前向传播
    pred = forward_propagation(x=X, w1=w1, b1=b1, w2=w2, b2=b2, w3=w3, b3=b3)
    # 反向传播
    backward_propagation(
        y=Y, pred=pred, w1=w1, b1=b1, w2=w2, b2=b2, w3=w3, b3=b3, alpha=0.01
    )
